package com.sfac.springboot.modules.common.controller;

import com.sfac.springboot.modules.common.entity.ExceptionLog;
import com.sfac.springboot.modules.common.service.ExceptionLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;

/**
 * @Description ExceptionController，捕获控制器所有异常并进行处理
 * @Author JiangHu
 * @Date 2022/8/25 10:33
 */
@ControllerAdvice
public class ExceptionController {

	public final static Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);
	@Autowired
	private ExceptionLogService exceptionLogService;

	/**
	 * 处理控制层所有的异常，根据 调用类型 和 异常类型 决定返回对象
	 * 接口调用异常，返回 403、404、500 对应的 Json 数据
	 * 页面调用异常，返回 403、404、500 对应的错误页面
	 */
	@ExceptionHandler(value = Exception.class)
	public ModelAndView handleException(
			HttpServletRequest request, Exception exception) {
		// 输出异常信息
		exception.printStackTrace();
		LOGGER.debug(exception.getMessage());

		// 插入异常信息
		insertExceptionLog(request, exception);

		// 根据异常类型，封装数据（状态码、消息、异常错误页面对应的路径）
		int status = 200;
		String message = "";
		String data = "";
		// 将 UnauthorizedException 作为没有权限异常处理
		if (exception instanceof UnauthorizedException) {
			status = 403;
			message = "没有权限。";
			data = "/common/403";
		} else {
			status = 500;
			message = "内部错误。";
			data = "/common/500";
		}

		// 判断请求类型，接口调用则返回json数据，反之返回错误页面
		if (checkCallType(request)) {
			ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
			mv.addObject("status", status);
			mv.addObject("message", message);
			mv.addObject("data", data);
			return mv;
		} else {
			return new ModelAndView(new RedirectView(data));
		}
	}

	// 判断请求是接口还是页面
	private boolean checkCallType(HttpServletRequest request) {
		HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
				"org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
		// 从方法上面获取 ResponseBody 注解
		ResponseBody[] annotations1 = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);
		// 从类上面获取 ResponseBody 注解
		ResponseBody[] annotations2 = handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);
		// 从类上面获取 RestController 注解
		RestController[] annotations3 = handlerMethod.getBeanType().getAnnotationsByType(RestController.class);
		return (annotations1.length > 0) || (annotations2.length > 0) || (annotations3.length > 0);
	}

	// 保存异常信息
	private void insertExceptionLog(
			HttpServletRequest request, Exception exception) {
		ExceptionLog exceptionLog = new ExceptionLog();
		exceptionLog.setIp(request.getRemoteAddr());
		exceptionLog.setPath(request.getServletPath());
		HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
				"org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
		exceptionLog.setClassName(handlerMethod.getBeanType().getName());
		exceptionLog.setMethodName(handlerMethod.getMethod().getName());
		exceptionLog.setExceptionType(exception.getClass().getSimpleName());
		String message = exception.getMessage();
		int length = 0;
		if (StringUtils.isNotBlank(message)) {
			length = message.length() > 200 ? 200 : message.length();
		}
		exceptionLog.setExceptionMessage(StringUtils.isNotBlank(message) ?
				exception.getMessage().substring(0, length) : "");

		exceptionLogService.insertExceptionLog(exceptionLog);
	}
}
